# -*- coding: utf-8 -*-
#
#= プロジェクトコントローラ
#WBS，ガントチャート，カレンダーなど基本的なデータの表示を行う．
#また，プロジェクトの追加，編集などを行う
#
class ProjectController < ApplicationController
  # 共通レイアウト定義
  layout "mainpage", :except=>[:block_list, :json_list_for_wbs, :json_list_for_gantt, :json_init_projects, :json_init_project_users, :json_init_project_comps, :json_init_project_logs, :json_init_tasks, :json_init_taskusers, :json_init_milestones, :json_init_events, :json_init_eventusers, :json_init_messages]

  # フィルタ定義
  before_filter :login_required, :except=>[:json_init_projects, :json_init_project_users, :json_init_project_comps, :json_init_project_logs, :json_init_tasks, :json_init_taskusers, :json_init_milestones, :json_init_events, :json_init_eventusers, :json_init_messages] # ログインチェック
  before_filter :get_project_info, :only=>[:index, :wbs, :gantt, :calendar, :file, :test_gantt, :json_list_for_wbs, :json_list_for_gantt, :new, :edit, :create, :update, :edit_active]
  before_filter :project_required, :only=>[:index, :wbs, :gantt, :calendar, :file, :list_get_wbs, :list_get_gantt, :edit, :destroy] # ユーザーチェック

  # ヘルパ定義
  include ApplicationHelper
  include ProjectHelper
  include TaskHelper

  #
  #プロジェクトコントローラのデフォルトアクション．
  #プロジェクトのダッシュボードを表示する．
  #コンポーネント表示により，タスク，イベント，直近の更新情報，プロジェクト参加メンバー一覧が表示される
  #
  def index
    if @g_projects.blank?
      redirect_to(:action => 'new')
    else
      @projects = @g_projects
      @project  = @g_cur_project
    end
  end


  #
  #=== WBSリストページを描画する
  #
  #現在のプロジェクト及び，アクティブなプロジェクトの一覧を渡して，
  #WBSの描画エリアを表示する
  #
  #実際のWBSリストはjsを通じて描画されるため，このアクションで表示されるのは枠組みのみ．
  #
  def wbs
    @projects = @g_projects
    @project  = @g_cur_project
  end

  #
  #=== ガントチャートページを描画する
  #
  #現在のプロジェクト及び，アクティブなプロジェクトの一覧を渡して，
  #ガントチャートの描画エリアを表示する
  #
  #実際のガントチャートはjsを通じて描画されるため，このアクションで表示されるのは枠組みのみ．
  #
  def gantt
    @projects = @g_projects
    @project  = @g_cur_project
  end

  #
  #=== カレンダーページを描画する
  #
  #現在のプロジェクト及び，アクティブなプロジェクトの一覧を渡して，
  #カレンダーの描画エリアを表示する．
  #
  #カレンダーのベースとなる日付などを表示して，データを表示する．
  #実際の描画は書き出されたデータを元にjsを通じて描画される．
  #
  def calendar
    @projects = @g_projects
    @project  = get_project_detail(@g_cur_project.id)
  end

  #
  #=== ファイル管理ページを表示する
  #
  #実際の描画はjsを通じて描画されるため，このアクションんで描画されるのは，枠組みのみ
  #
  def file
    @project = @g_cur_project
  end




  #
  #プロジェクトの一覧を表示する
  #(未使用？)
  #
  #ログイン中のユーザが参加しているプロジェクトの一覧を取得．
  #同時に関連するタスク，イベント，マイルストーンを取得する
  #
  def block_list
    opt = {
      :include => [
        :dat_projectusers,
        {:dat_projectcomps => [:dat_task, :dat_event, :dat_milestone]}
      ],
      :conditions => ["dat_projectusers.user_id = ? ", session["user"]["id"]],
      :order      => "dat_projects.id desc"
    }
    @projects = DatProject.find(:all, opt)
  end


  #
  #WBSの一覧データをjson形式で返却する
  #
  #現在アクティブなプロジェクトのwbsデータを取得して，json形式で返却する．
  #project/wbsページより，ajaxを使用して呼び出される
  #
  def json_list_for_wbs
    project = get_project_detail(@g_cur_project.id)
    items   = project.dat_projectcomps.map do |pc|
      {
        :task_kbn          => pc.task_kbn,
        :task_cd           => disp_task_id( pc ),
        :item_name         => disp_detail_name( pc ),
        :user_name         => disp_charge_user_name( pc ),
        :comp_exp_date     => disp_comp_exp_date( pc ),
        :situation         => disp_situation( pc ),
        :priority_kbn      => pc.task_kbn==1 ? pc.dat_task.priority_kbn : "",
        :progress_kbn      => pc.task_kbn==1 ? pc.dat_task.progress_kbn : "",
        :create_user_name  => disp_create_user_name( pc ),
        :client_user_name  => disp_client_user_name( pc ),
        :id                => pc.id,
        :class_word1       => h(pc.class_word1),
        :class_word2       => h(pc.class_word2),
        :class_word3       => h(pc.class_word3)
      }
    end
    result = {:items => items}
    render :text => result_for_json(true, '', result)
  end


  #
  #WBSの一覧データをjson形式で返却する
  #
  #現在アクティブなプロジェクトのwbsデータを取得して，json形式で返却する．
  #project/ganttページより，ajaxを使用して呼び出される
  #
  #プロジェクトアイテムの種類により範囲表示時に使用する開始日時と終了日時カラムが異なるため，
  #種類に応じたデータをstart_date，end_dateに格納して返却
  #
  def json_list_for_gantt
    project = get_project_detail(@g_cur_project.id)
    items = project.dat_projectcomps.map do |pc|
      start_date = pc.task_kbn==1 ? pc.dat_task.start_date : ( pc.task_kbn==2 ? pc.dat_milestone.mils_date : ( pc.task_kbn==3 ? pc.dat_event.start_date : "" ) )
      end_date   = pc.task_kbn==1 ? pc.dat_task.end_date : ( pc.task_kbn==2 ? pc.dat_milestone.mils_date : ( pc.task_kbn==3 ? pc.dat_event.start_date : "" ) )
      {
        :task_kbn          => pc.task_kbn,
        :task_cd           => disp_task_id( pc ),
        :item_name         => disp_detail_name( pc ),
        :user_name         => disp_charge_user_name( pc ),
        :comp_exp_date     => disp_comp_exp_date( pc ),
        :situation         => disp_situation( pc ),
        :priority_kbn      => pc.task_kbn==1 ? pc.dat_task.priority_kbn : "",
        :progress_kbn      => pc.task_kbn==1 ? pc.dat_task.progress_kbn : "",
        :start_date        => (start_date ? start_date.strftime('%Y-%m-%d') : ''),
        :end_date          => (end_date ? end_date.strftime('%Y-%m-%d') : ''),
        :create_user_name  => disp_create_user_name( pc ),
        :client_user_name  => disp_client_user_name( pc ),
        :id                => pc.id,
        :class_word1       => h(pc.class_word1),
        :class_word2       => h(pc.class_word2),
        :class_word3       => h(pc.class_word3)
      }
    end
    result = {:project => project, :items => items}
    render :text => result_for_json( true, '', result )
  end


  #
  #=== プロジェクトの新規作成ページを表示する
  #
  #空のプロジェクトデータを生成してテンプレートに渡す．
  #また，wbsテンプレートの一覧を取得する
  #
  def new
    # 新規プロジェクトオブジェクト生成
    @project = DatProject.new
    @project.dat_projectusers.build

    # テンプレートデータ一覧取得
    @templates = MstTemplate.find(:all)
    
    # 編集用ビューを指定
    render :action => "edit"
  end


  #
  #=== プロジェクトの編集ページを表示
  #
  #指定プロジェクトの編集ページを表示する
  #
  #idで指定されたプロジェクトのデータを取得する．
  #また，同時にそのプロジェクトに参加しているユーザの一覧を取得する．
  #
  #idが指定されていない場合は，新規作成ページへリダイレクト
  #
  def edit
    unless params[:id].nil?
      # 登録済みのプロジェクトオブジェクト取得
      id = params[:id]
      opt = {
        :conditions => [" dat_projects.id=? ", id],
        :include    => :dat_projectusers
      }
      @project = DatProject.find(:first, opt)
      # 参加ユーザーが登録されていない場合、新規生成
      @project.dat_projectusers.build if @project.dat_projectusers.size == 0
      # テンプレートデータ一覧取得
      @templates = MstTemplate.find(:all)
    else
      # IDが指定されていない場合、新規作成処理へリダイレクト
      redirect_to :action => 'new'
    end
  end


  #
  #=== プロジェクトの新規作成
  #
  #フォームから渡されたデータを元にプロジェクトを作成する．
  #
  #ユーザメールアドレスがある場合で，すでに登録されているユーザであれば招集メールを送信する．
  #ユーザとしてまだ登録されていないユーザにはサインアップ通知メールを送信する．
  #
  #テンプレートが指定されている場合には，テンプレートを元にプロジェクトアイテムを生成して登録する．
  #また，指定パスにファイル共有ディレクトリを生成する．
  #
  def create
    # プロジェクトオブジェクト生成
    # フォームパラメータから新規プロジェクトオブジェクトを生成
    project = DatProject.new(params[:project])
    project.create_user_id = session["user"]["id"]
    project.update_user_id = session["user"]["id"]

    #-----------------------------
    # 参加ユーザー生成
    #-----------------------------
    # 登録者自身もプロジェクトユーザーとして生成
    if ! session["user"]["id"].nil?
      user = MstUser.find(session["user"]["id"])
      projectuser = project.dat_projectusers.build()
      projectuser.email = user.email
      projectuser.user_id = user.id
      projectuser.create_user_id = project.create_user_id
    end
    # 参加ユーザー指定時、プロジェクトユーザーオブジェクトを生成
    for user in params[:users]
      next if user.empty?
      next if user == session["user"]["email"]
      # buildにて生成し、ここでは保存しない
      projectuser = project.dat_projectusers.build(:email=>user)
      projectuser.create_user_id = project.create_user_id
      if mstusr = MstUser.find_by_email(user)
        # 既にユーザーマスタに登録済みのメールアドレスの場合、ユーザーIDを更新
        projectuser.user_id = mstusr.id

        # プロジェクト召集メールを送信する
        if ! mstusr.email.nil? && ! mstusr.email.empty?
          AppMailer.deliver_mail_invite_project( mstusr, {:subject=>app_localized_message( :label, :invite_project_mail_subject ), :project_name=>params[:project][:project_name], :url_login=>url_for(:controller=>:login, :action=>:login )} )
        end
      else
        # 招待メールを送信する
        if ! projectuser.email.nil? && ! projectuser.email.empty?
          AppMailer.deliver_mail_invite( projectuser, {:subject=>app_localized_message( :label, :invite_mail_subject ), :url_signup=>url_for(:controller=>:login, :action=>:signup )} )
        end
      end
    end
  
    #-----------------------------
    # テンプレートデータから、プロジェクトを構成
    #-----------------------------
    if ! params[:template][:id].nil? && ! params[:template][:id].empty?
      # 選択されたテンプレートデータを取得
      template = MstTemplate.find(:first,
                                  :conditions=>[" mst_templates.id=? ", params[:template][:id]], 
                                  :include=>[{:mst_compositions=>[:mst_tptask, :mst_tpmilestone, :mst_tpevent]}])
      # テンプレートデータを新規オブジェクトにコピー（ここでは保存されない）
        project.copyFromTemplate(template)
    end

    #-----------------------------
    # プロジェクトデータ保存実施（関連データもここで保存）
    #-----------------------------
    if ! project.save
      @project = project 
      @templates = MstTemplate.find(:all)
      render :action => 'edit'
      return 
    end

    #-----------------------------
    # FTPディレクトリ作成
    #-----------------------------
    path = get_project_files_root(project.id)
    #Dir.mkdir(path)
    FileUtils.mkdir_p(path)

    # プロジェクトコントローラデフォルトページへリダイレクト
    redirect_to :action => 'index'
  end


  #
  #=== プロジェクト情報の更新
  #
  #指定されたプロジェクトの情報を更新する．
  def update
    if params[:project][:id].nil? || params[:project][:id].empty?
      # IDが指定されていない場合、エラー処理
      # ????? エラー処理 ????? 
    else
      #-----------------------------
      # 登録済みのプロジェクトデータを取得
      #-----------------------------
      id = params[:project][:id]
      project = DatProject.find(:first,
                                :conditions=>[" dat_projects.id=? ", id],
                                :include=>:dat_projectusers)

      project.update_user_id = session["user"]["id"]

      #-----------------------------
      # 参加ユーザー生成
      #-----------------------------
#      # 参加ユーザーを一旦削除
#      project.dat_projectusers.clear
      projectusers = Array.new
      project.dat_projectusers.each do |puser|
        projectusers.push puser.email
      end

      # 参加ユーザー指定時、プロジェクトユーザーオブジェクトを生成
      for user in params[:users]
        next if user.empty?
        next if ! projectusers.delete(user).nil?
        next if user == session["user"]["email"]

        # buildにて生成し、ここでは保存しない
        projectuser = project.dat_projectusers.build(:email=>user)
        projectuser.create_user_id = project.update_user_id
        if mstusr = MstUser.find_by_email(user)
          # 既にユーザーマスタに登録済みのメールアドレスの場合、ユーザーIDを更新
          projectuser.user_id = mstusr.id

          # プロジェクト召集メールを送信する
          if ! mstusr.email.nil? && ! mstusr.email.empty?
            AppMailer.deliver_mail_invite_project( mstusr, {:subject=>app_localized_message( :label, :invite_project_mail_subject ), :project_name=>params[:project][:project_name], :url_login=>url_for(:controller=>:login, :action=>:login )} )
          end
        else
          # 招待メールを送信する
          if ! projectuser.email.nil? && ! projectuser.email.empty?
            AppMailer.deliver_mail_invite( projectuser, {:subject=>app_localized_message( :label, :invite_mail_subject ), :url_signup=>url_for(:controller=>:login, :action=>:signup )} )
          end
        end
      end
#      # 登録者自身もプロジェクトユーザーとして生成
#      if ! session["user"]["id"].nil?
#        user = MstUser.find(session["user"]["id"])
#        projectuser = project.dat_projectusers.build()
#        projectuser.email = user.email
#        projectuser.user_id = user.id
#        projectuser.create_user_id = project.create_user_id
#      end

      #-----------------------------
      # プロジェクトデータ保存実施（関連データもここで保存）
      #-----------------------------
      if ! project.update_attributes(params[:project])
        # ????? エラー処理 ????? 
        @project = project 
        @templates = MstTemplate.find(:all)
        render :action => 'edit'
        return 
      end
      
      # 参加ユーザー削除
      if projectusers.size > 0
        conditions = " project_id = ? AND email in (__emails__)"
        emails   = []
        prepared = ''
        projectusers.each_with_index do |email, index|
          logger.debug email
          emails   << email
          prepared << ((index > 0) ? ',' : '') + '?'
        end
        conditions = [conditions.gsub(/__emails__/, prepared), project.id] + emails
        DatProjectuser.destroy_all( conditions )
      end
    end

    # プロジェクトコントローラデフォルトページへリダイレクト
    redirect_to :action => 'index'
  end


  #
  #=== プロジェクトの削除
  #
  #プロジェクトの削除を行う．
  #関連したデータも全て削除する．
  #
  def destroy
    if params[:project][:id].nil? || params[:project][:id].empty?
    elsif my_project?(params[:project][:id])
      #-----------------------------
      # プロジェクトデータ削除実施（関連データも削除される）
      #-----------------------------
      id = params[:project][:id]
      if ! DatProject.destroy(id)
        # ????? エラー処理 ????? 
        @project = project 
        @templates = MstTemplate.find(:all)
        render :action => 'edit'
        return 
      end
    else
      render :text => 'Permission Denied'
      return
    end

    #-----------------------------
    # FTPディレクトリ削除 ????? 空にしないとだめ ?????
    #-----------------------------
#    Dir.rmdir("#{RAILS_ROOT}/public/attach/" + id) 

    # プロジェクトコントローラデフォルトページへリダイレクト
    redirect_to :action => 'index'
  end

  #
  #=== 表示するタブの管理を行うページを表示
  #
  #ログイン中のユーザが参加しているプロジェクトの一覧を表示する．
  #現在アクティブなプロジェクトの一覧も同時に取得して，表示の際に，タブに表示するか削除するかのトリガ表示を切り替える
  #
  def edit_active
    opt = {
     :include    => [{:dat_projectusers=>:mst_user}],
     :conditions => ["dat_projects.valid_flg = 1 AND dat_projects.id in (SELECT pu.project_id FROM dat_projectusers pu WHERE pu.user_id = ? ) ", session["user"]["id"]],
     :order      => "dat_projects.start_date desc, delivery_date desc"
    }
    @projects = DatProject.find(:all, opt)
    @active_project_ids = @g_projects.map {|p| p.id}
  end


  #
  #=== アクティブなプロジェクトの更新を行う
  #
  #指定されたプロジェクト及び，ユーザIDを元にプロジェクト所属ユーザを取得し，
  #そのユーザの該当プロジェクトアクティブフラグを切り替える．
  #
  #結果をjson形式で返却する．
  #
  def update_active

    project      = DatProject.find_project_and_users(params[:id],session["user"]["id"])
    projectusers = project.dat_projectusers[0]
    projectusers.update_attribute(:active_flg, params[:active_flg])

    # JSONデータ生成
    result  = {
      :project_id   => project.id,
      :project_name => project.project_name,
      :active_flg   => projectusers.active_flg
    }
    render :text => result_for_json(true, '', result)
  end


  #
  #=== プロジェクトの共有ディレクトリ一覧を取得して返却
  #
  #指定されたディレクトリ内のディレクトリ一覧を取得してjson形式で返却．
  #
  def list_dirs
    if my_project?(params[:id]) and valid_node?(params[:id], params[:node])
      rootpath = get_project_files_root(params[:id])
      files    = get_dir_list(rootpath + params[:node].to_s, params[:node])
      files ||= []
      result = files.to_json
    else
      result = 'Permission Denied'
    end
    render :text => result
  end

  #
  #=== プロジェクトの共有ファイル一覧を取得して返却
  #
  #指定されたディレクトリ内のファイル一覧を取得してjson形式で返却．
  #
  def list_files
    if my_project?(params[:id]) and valid_node?(params[:id], params[:node])
      rootpath = get_project_files_root(params[:id])
      files    = get_file_list(rootpath + params[:node].to_s, params[:node])
      files ||= []
      result = {:items => files}.to_json
    else
      result = 'Permission Denied'
    end
    render :text => result
  end


  private

  #
  #=== プロジェクトの詳細データを取得
  #
  #指定されたプロジェクトのデータを取得して，json形式で返却．
  #同時に関連するタスク，マイルストーン，イベントデータを取得して返却．
  #
  #idが指定されていない場合には，空のプロジェクトオブジェクトを生成して返却
  #
  def get_project_detail(id)
    if id.nil?
      # ID指定なしの場合、空のプロジェクトを生成
      project = DatProject.new
    else
      #-----------------------------
      # 指定されたプロジェクトデータを取得
      # （トップレベルのプロジェクト構成およびタスク、イベント、分類、マイルストーンデータも同時に取得）
      #-----------------------------
      opt = {
        :conditions => ["dat_projects.id = ?", id],
        :order      => "dat_projectcomps.line_no asc",
        :include    => [{
            :dat_projectcomps => [
              {:dat_task => [{:dat_user_main   => :mst_user}, {:dat_user_client => :mst_user}]},
              :dat_milestone,
              :dat_event,
              :mst_user_create
            ]
          }]
      }
      project = DatProject.find(:first, opt)
    end
    return project
  end


  #
  #=== プロジェクトユーザチェック
  #
  #指定されたプロジェクトにログイン中のユーザが参加しているかどうかをチェックする．
  #
  #参加している場合にはtrue，参加していない場合にはfalseを返却する．
  #
  def project_required
    ret = false
    if not @g_cur_project.nil?
      @g_cur_project.dat_projectusers.each do |user|
        if user.user_id == session["user"]["id"]
          ret = true
          break
        end
      end
      if @g_cur_project.dat_projectusers.size <= 0
        ret = true
      end
    elsif not @g_projects.nil?
      if @g_projects.size == 0 and params[:id].nil?
        ret = true
      end
    elsif not params[:id].nil?
      project  = DatProject.find_project_and_users(params[:id],session["user"]["id"])
      if project.nil? 
        ret = false
      else
        ret = true
      end
    else
      ret = true
    end
    unless ret
      render :text=>"Permission Denied!!"
    end
    return ret
  end



  #
  #=== ディレクトリ一覧を取得して返却
  #
  #プロジェクトのファイル共有ディレクトリのディレクトリ一覧を取得して返却．
  #
  def get_dir_list(path, parentnode)

    list = []

    iconv = Iconv.new($SYSTEM_ENCODING, $FILESYSTEM_ENCODING)
    iconv2 = Iconv.new($FILESYSTEM_ENCODING, $SYSTEM_ENCODING)
    path_conv = iconv2.iconv(path)

    #プロエジェクトの該当ディレクトリが無い場合、新たにディレクトリを作る
    FileUtils.mkdir_p(path_conv)

    Dir.foreach(path_conv) do |f|
      next if (f == "." or f == "..")
      ftype = File.ftype(path_conv + f)
      next if ftype != "directory"
      entries = Dir.entries(path_conv + f)

      list.push({
        :text => iconv.iconv(f),
        :id   => parentnode + iconv.iconv(f) + "/",
        :leaf => false, #entries.length==2 ? true : false,
        :cls  => "folder"
      })
    end
    return list
  end


  #
  #=== ファイル一覧を取得
  #
  #プロジェクトのファイル共有のファイル一覧を取得する
  #
  def get_file_list(path, parentnode)
    list = []
    iconv = Iconv.new($SYSTEM_ENCODING, $FILESYSTEM_ENCODING)
    iconv2 = Iconv.new($FILESYSTEM_ENCODING, $SYSTEM_ENCODING)
    path_conv = iconv2.iconv(path)

    Dir.foreach(path_conv) do |f|
      next if f == "."
      next if f == ".."

      fstat = File::Stat.new(path_conv + f) 

      #ディレクトリのサイズは表示しないために分岐を追加
      ftype = File.ftype(path_conv + f)
      if ftype == "directory"
        list.push({
          :id    => parentnode + iconv.iconv(f) ,
          :fname => iconv.iconv(f),
          :ftype => fstat.ftype,
          :size  => "",
          :mtime => fstat.mtime,
          :mode  => fstat.mode
        })
      else
        list.push({
          :id    => parentnode + iconv.iconv(f) ,
          :fname => iconv.iconv(f),
          :ftype => fstat.ftype,
          :size  => fstat.size,
          :mtime => fstat.mtime,
          :mode  => fstat.mode
        })
      end
    end
    return list
  end

end
